#include "es7210_audio_adc.h"

static const char *TAG = "es7210_audio";
static i2s_chan_handle_t i2s_rx_handle = NULL;

static i2s_chan_handle_t es7210_i2s_init(void)
{
    //i2s_chan_handle_t i2s_rx_chan = NULL;  // 定义接收通道句柄
    ESP_LOGI(TAG, "Create I2S receive channel");
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER); // 配置接收通道
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, NULL, &i2s_rx_handle)); // 创建i2s通道

    ESP_LOGI(TAG, "Configure I2S receive channel to TDM mode");
    i2s_tdm_config_t i2s_tdm_rx_cfg = {  
        .slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(EXAMPLE_I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO, EXAMPLE_I2S_TDM_SLOT_MASK),
        .clk_cfg  = {
            .clk_src = I2S_CLK_SRC_DEFAULT,
            .sample_rate_hz = EXAMPLE_I2S_SAMPLE_RATE,
            .mclk_multiple = EXAMPLE_I2S_MCLK_MULTIPLE
        },
        .gpio_cfg = {
            .mclk = EXAMPLE_I2S_MCK_IO,
            .bclk = EXAMPLE_I2S_BCK_IO,
            .ws   = EXAMPLE_I2S_WS_IO,
            .dout = -1, // ES7210 only has ADC capability
            .din  = EXAMPLE_I2S_DI_IO
        },
    };

    ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(i2s_rx_handle, &i2s_tdm_rx_cfg)); // 初始化I2S通道为TDM模式

    return i2s_rx_handle;
}

static esp_err_t es7210_adc_init(void)
{
    /* 初始化I2C接口 */
    ESP_ERROR_CHECK(bsp_i2c_init());

    // 创建es7210器件句柄
    es7210_dev_handle_t es7210_handle = NULL;
    es7210_i2c_config_t es7210_i2c_conf = {
        .i2c_port = BSP_I2C_NUM,
        .i2c_addr = EXAMPLE_ES7210_I2C_ADDR
    };
    ESP_ERROR_CHECK(es7210_new_codec(&es7210_i2c_conf, &es7210_handle));

    // 初始化es7210芯片
    ESP_LOGI(TAG, "Configure ES7210 codec parameters");
    es7210_codec_config_t codec_conf = {
        .i2s_format = EXAMPLE_I2S_TDM_FORMAT,
        .mclk_ratio = EXAMPLE_I2S_MCLK_MULTIPLE,
        .sample_rate_hz = EXAMPLE_I2S_SAMPLE_RATE,
        .bit_width = (es7210_i2s_bits_t)EXAMPLE_I2S_SAMPLE_BITS,
        .mic_bias = EXAMPLE_ES7210_MIC_BIAS,
        .mic_gain = EXAMPLE_ES7210_MIC_GAIN,
        .flags.tdm_enable = true
    };
    ESP_ERROR_CHECK(es7210_config_codec(es7210_handle, &codec_conf));
    ESP_ERROR_CHECK(es7210_config_volume(es7210_handle, EXAMPLE_ES7210_ADC_VOLUME));

    return ESP_OK;
}

/* 挂载SD卡 */
sdmmc_card_t *sdcard_mount();


/**
 * @brief 录制音频数据并保存为WAV格式文件
 * @param i2s_rx_chan I2S接收通道句柄，用于从音频设备读取数据
 * @return esp_err_t ESP_OK表示成功，其他错误码表示失败
 */
static esp_err_t record_wav(i2s_chan_handle_t i2s_rx_handle)
{
    // 检查I2S接收通道句柄是否有效，无效则返回失败
    // ESP_RETURN_ON_FALSE：如果第一个参数为假，则返回第二个参数，并打印日志
    ESP_RETURN_ON_FALSE(i2s_rx_handle, ESP_FAIL, TAG, "invalid i2s channel handle pointer");
    esp_err_t ret = ESP_OK;

    // 计算音频的字节率（每秒传输的字节数）
    // 公式：采样率 × 声道数 × 采样位数 ÷ 8（将位转换为字节）
    uint32_t byte_rate = EXAMPLE_I2S_SAMPLE_RATE * EXAMPLE_I2S_CHAN_NUM * EXAMPLE_I2S_SAMPLE_BITS / 8;
    // 计算WAV文件的总大小（字节）：字节率 × 录制时长（秒）
    uint32_t wav_size = byte_rate * SD_RECORD_TIME_SEC;

    // 生成WAV文件的头部信息
    // WAV_HEADER_PCM_DEFAULT：根据参数生成PCM格式的WAV头部
    const wav_header_t wav_header =
        WAV_HEADER_PCM_DEFAULT(wav_size, EXAMPLE_I2S_SAMPLE_BITS, EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_CHAN_NUM);


    ESP_LOGI(TAG, "Opening file %s", SD_RECORD_FILE_PATH);
    // 打开文件用于写入（"w"表示创建或截断文件，以二进制方式写入）
    // 路径由SD卡挂载点和文件名拼接而成
    FILE *f = fopen(SD_MOUNT_POINT SD_RECORD_FILE_PATH, "w");
    // 检查文件是否成功打开，失败则返回错误
    ESP_RETURN_ON_FALSE(f, ESP_FAIL, TAG, "error while opening wav file");

    /* 写入WAV文件头部 */
    // 写入头部信息到文件，失败则跳转到err标签处理
    // ESP_GOTO_ON_FALSE：如果条件为假，设置ret为错误码并跳转到err
    ESP_GOTO_ON_FALSE(fwrite(&wav_header, sizeof(wav_header_t), 1, f), ESP_FAIL, err,
                      TAG, "error while writing wav header");

    /* 开始录制音频 */
    // 记录已写入文件的字节数
    size_t wav_written = 0;
    // 定义I2S原始数据读取缓冲区（16位采样，大小4096个元素，即8192字节）
    static int16_t i2s_readraw_buff[4096];
    // 启用I2S接收通道，失败则跳转到err标签
    ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_handle), err, TAG, "error while starting i2s rx channel");

    // 循环读取并写入数据，直到达到预设的WAV文件总大小
    while (wav_written < wav_size) {
        // 每秒打印一次录制进度（通过字节率判断是否进入新的一秒）
        if(wav_written % byte_rate < sizeof(i2s_readraw_buff)) {
            ESP_LOGI(TAG, "Recording: %"PRIu32"/%ds", wav_written/byte_rate + 1, SD_RECORD_TIME_SEC);
        }

        // 记录实际从I2S读取的字节数
        size_t bytes_read = 0;
        /* 从ES7210（音频ADC芯片）读取原始采样数据 */
        // 读取数据到缓冲区，超时时间1000ms，失败则跳转到err
        ESP_GOTO_ON_ERROR(i2s_channel_read(i2s_rx_handle, i2s_readraw_buff, sizeof(i2s_readraw_buff), &bytes_read,
                          pdMS_TO_TICKS(1000)), err, TAG, "error while reading samples from i2s");

        /* 将采样数据写入WAV文件 */
        // 写入读取到的字节数到文件，失败则跳转到err
        ESP_GOTO_ON_FALSE(fwrite(i2s_readraw_buff, bytes_read, 1, f), ESP_FAIL, err,
                          TAG, "error while writing samples to wav file");

        // 更新已写入的总字节数
        wav_written += bytes_read;
    }

// 错误处理标签：无论成功或失败，都执行以下清理操作
err:
    // 禁用I2S接收通道
    i2s_channel_disable(i2s_rx_handle);
    // 打印日志，提示录制完成并刷新文件缓冲区
    ESP_LOGI(TAG, "Recording done! Flushing file buffer");
    // 关闭文件（自动刷新缓冲区）
    fclose(f);

    // 返回结果（成功或错误码）
    return ret;
}



void es7210(void)
{
    /* 初始化I2S接口 */
    i2s_chan_handle_t i2s_rx_handle = es7210_i2s_init();  
    /* 初始化es7210芯片 */
    es7210_adc_init();
    sdmmc_card_t *sdmmc_card = sdcard_mount();
    /* 录音 */
    esp_err_t err = record_wav(i2s_rx_handle);
    /* 弹出SD卡 */
    esp_vfs_fat_sdcard_unmount(SD_MOUNT_POINT, sdmmc_card);
    if(err == ESP_OK) {
        ESP_LOGI(TAG, "Audio was successfully recorded into "SD_RECORD_FILE_PATH
                      ". You can now remove the SD card safely");
    } else {
        ESP_LOGE(TAG, "Record failed, "SD_RECORD_FILE_PATH" on SD card may not be playable.");
    }
}